package sql.query;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import sql.utils.Operator;
import sql.parser.SyntaxNode;
import sql.parser.SyntaxNodeType;
import sql.utils.Utils;
import sql.ast.*;

import java.util.List;
import java.util.Map;

public class Transformer {
    private ObjectVisitor objectVisitor;

    public Transformer(ObjectVisitor objectVisitor) {
        this.objectVisitor = objectVisitor;
    }

    public ObjectQuery transformObjectQuery(SyntaxNode syntaxNode) {
        return transformObjectQuery0(syntaxNode);
    }

    public ObjectQuery transformObjectQuery0(SyntaxNode syntaxNode) {
        SyntaxNode fromNode = syntaxNode.getFromNode();
        String tableName = StringUtils.isBlank(fromNode.getAlias()) ? fromNode.getTokenAsString() : fromNode.getAlias();
        Node conditionNode = transformConditionNode(syntaxNode.getWhereNode(), tableName);
        SyntaxNode selectNode = syntaxNode.getSelectNode();
        SyntaxNode orderByNode = syntaxNode.getOrderByNode();
        SyntaxNode limitNode = syntaxNode.getLimitNode();
        return new ObjectQuery(this.objectVisitor, tableName, selectNode, conditionNode, orderByNode, limitNode);
    }

    public Node transformConditionNode(SyntaxNode syntaxNode, String tableName) {
        if(syntaxNode == null) {
            return new ParallelNode();
        }
        if(syntaxNode.getNodeType() == SyntaxNodeType.Condition) {
            List<SyntaxNode> children = syntaxNode.getChildren();
            SyntaxNode left = children.get(0);
            Operator op = Utils.getOperator(children.get(1).getTokenAsString());
            SyntaxNode right = children.get(2);
            CompareNode compareNode;
            if(right.getSelectNode() != null) {
                ObjectQuery objectQuery = transformObjectQuery0(right);
                compareNode = new CompareNode(left.getTokenAsString(), op, row-> {
                    Map<String, Map> args = Maps.newHashMap();
                    args.put(tableName, row);
                    return objectQuery.execStringList(args);
                });
            } else {
                Object token = right.getToken();
                List<Object> rightParams = Lists.newArrayList();
                if(token == null) {
                    List<SyntaxNode> params = right.getChildren();
                    if(params.isEmpty()) {
                        throw new RuntimeException("in条件中参数为空");
                    }
                    for (SyntaxNode param : params) {
                        rightParams.add(param.getToken());
                    }
                } else {
                    rightParams.add(token);
                }
                compareNode = new CompareNode(left.getTokenAsString(), op, row->  rightParams);
            }
            return compareNode;
        } else {
            List<SyntaxNode> children = syntaxNode.getChildren();
            if(children.size() >= 3) {
                SyntaxNode opNode = children.get(1);
                if(opNode.getNodeType() == SyntaxNodeType.Or) {
                    OrNode orNode = new OrNode();
                    for (SyntaxNode child : children) {
                        if(child.getNodeType() != SyntaxNodeType.Or) {
                            orNode.addChild(transformConditionNode(child, tableName));
                        }
                    }
                    return orNode;
                } else if(opNode.getNodeType() == SyntaxNodeType.And) {
                    AndNode andNode = new AndNode();
                    for (SyntaxNode child : children) {
                        if(child.getNodeType() != SyntaxNodeType.And) {
                            andNode.addChild(transformConditionNode(child, tableName));
                        }
                    }
                    return andNode;
                } else {
                    throw new RuntimeException("逻辑运算符不存在");
                }
            } else if(children.size() == 1) {
                SyntaxNode conditionNode = children.get(0);
                return transformConditionNode(conditionNode, tableName);
            } else {
                throw new RuntimeException("节点类型错误");
            }
        }

    }
}

